﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using SoftwareConsulting.BI11.Studio.InternalServices;
using SoftwareConsulting.BI11.Studio.Core.Project.Metadata;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.NumericalMethods;
using SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.Extensions;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.BasicEntities;
using SoftwareConsulting.BI11.Studio.DataTransfer;
using SoftwareConsulting.BI11.Controls.Metadata;

namespace SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.ClassificationProblems
{
    /// <summary>
    /// Класс диалога редактирования обучающей выборки Задачи классификации с обучением
    /// </summary>
    public partial class ClassificationProblemWithTrainingEditTrainingDialog : RestorableForm
    {
        private ProjectExplorerForm _projectExplorer; //связь с обозревателем проекта
        private IClassificationProblemWithTraining _problem; //задача классификации с обучением                        
        private bool _gridInitialized; //флаг проинициализированного грида
        private bool _bygroups; //флаг расположения признаков по группам
        private ToolStripItem _arrangeByGroupsOrIndicatorPositionItem; //пункт расположения признаков по группам или по положению в показателе

        /// <summary>
        /// Обновляет количество обучающих записей
        /// </summary>
        private void RefreshTrainingCount()
        {
            int offset = TrainingGrid.NewRowIndex == -1 ? 0 : 1;//минус 1, т.к. последняя строка это NewRow
            TrainingCountBox.Text = (TrainingGrid.Rows.Count - offset).ToString();
        }

        /// <summary>
        /// Инициализирует диалог
        /// </summary>
        private void Initialize()
        {
            _gridInitialized = false;
            this.SetObjectViewerCaption(this.Text, _problem.ToString());
            this.Icon = MetadataObjectKind.ClassificationProblem.GetIcon();
            var rows = TrainingGrid.Rows;
            rows.Clear();
            var classes = _problem.ClassesNames;
            var attributes = _problem.AttributesNames;
            if ((classes == null) || (classes.GetIndicatorValuesCount() == 1)
                || (attributes == null) || (attributes.GetIndicatorValuesCount() == 1))
            {
                OKButton.Enabled = false;
                RefreshTrainingCount();
                return;
            }
            var columns = TrainingGrid.Columns;
            columns.Clear();
            var classColumn = new DataGridViewComboBoxColumn();
            classColumn.Name = "class";
            classColumn.HeaderText = classes.ToString();
            var classItems = classColumn.Items;
            for (int i = 1; i < classes.GetIndicatorValuesCount(); i++)//с первого, т.к. 0й - [не важно]                
            {
                classItems.Add(classes.GetIndicatorValueByPosition(i).Value as string);
            }
            columns.Add(classColumn);
            classColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
            classColumn.Frozen = true;
            classColumn.CellTemplate.Value = classItems[0];            
            for (int i = 1; i < attributes.GetIndicatorValuesCount(); i++)//с первого, т.к. 0й - [не важно]                
            {
                string attributeName = attributes.GetIndicatorValueByPosition(i).Value as string;
                columns.Add("attribute" + i.ToString(), attributeName);
                columns[columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;            
            }
            ClassificationObjectInfo[] records = _problem.GetTrainingCacheRecords(true);
            for (int i = 0; i < records.Length; i++)
            {
                var record = records[i];                
                object[] vals = new object[TrainingGrid.Columns.Count];
                record.Attributes.CopyTo(vals, 1);
                for (int j = 1; j < vals.Length; j++)
                {
                    if ((double)vals[j] == Constants.NullDouble)
                        vals[j] = string.Empty;
                }
                vals[0] = classes.GetIndicatorValueByPosition(record.Class + 1).Value as string;//+1 в классе, т.к. первый - [не важно]
                int rowIndex = rows.Add(vals);
                (rows[rowIndex].Cells[0] as DataGridViewComboBoxCell).Value = vals[0];//почему-то предыдущее присвоение для комбика не работает
            }            
            TrainingGrid.Rows[TrainingGrid.NewRowIndex].Cells[0].Value = classItems[0];
            _gridInitialized = true;
            RefreshTrainingCount();
            _bygroups = false;
            var items = TrainingGrid.ContextMenuStrip.Items;
            var separator = new ToolStripSeparator();
            if (_problem.Method == ClassificationProblemMethod.SimpleClassificator)
            {                                
                items.Add(separator);
                items.Add(Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_ATTRIBUTESBYGROUPS);
                _arrangeByGroupsOrIndicatorPositionItem = items[items.Count - 1];
                _arrangeByGroupsOrIndicatorPositionItem.Click += new EventHandler(_arrangeByGroupsOrIndicatorPositionItem_Click);
            }
            separator = new ToolStripSeparator();
            items.Add(separator);
            ToolStripItem exportItem = items.Add(Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_TRAININGEXPORT);
            exportItem.Click += new EventHandler(exportItem_Click);
            if (records.Length != _problem.TrainingCacheCount)
                GeneralMethods.ShowInformation(Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_TRAININGCOUNT_OLD);
        }

        /// <summary>
        /// Экспортирует записи кэша обучения в таблицу
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void exportItem_Click(object sender, EventArgs args)
        {
            List<ClassificationObjectInfo> infos = ProcessChanges();
            if (infos == null)
                return; //что-то некорректно ввели, экспортировать не получится
            ClassificationProblemWithTrainingExporter exporter = new ClassificationProblemWithTrainingExporter(_problem, _projectExplorer.ProjectLink.RepositoryLink);
            ITable table = null;
            try
            {
                table = exporter.ExportToTable(infos, true);
            }
            catch (BIDataTransferException ex)
            {
                GeneralMethods.ShowUserError(ex.Message);
                return;
            }
            if (table != null)//бывает равен null если transfer в точку назначения провалился
                _projectExplorer.OpenObject(table);                    
        }

        /// <summary>
        /// Располагает признаки по группам или по положению в показателе
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void _arrangeByGroupsOrIndicatorPositionItem_Click(object sender, EventArgs args)
        {            
            _bygroups = !_bygroups;            
            var columns = TrainingGrid.Columns;
            if (_bygroups)
            {
                List<AttributeInfo> infos = _problem.GetAttributesInfoSortedByGroups();
                for (int i = 0; i < infos.Count; i++)
                {
                    var info = infos[i];
                    var group = info.Group;
                    group = ClassificationProblemGroupsProcessor.CorrectGroup(group);
                    var column = columns[info.PositionInIndicator + 1];
                    column.DisplayIndex = i + 1;//+1, т.к. первый столбец это столбец класса
                    column.DefaultCellStyle.BackColor = ClassificationProblemGroupsProcessor.Colors[group];
                }
            }
            else
            {
                for (int i = 1; i < columns.Count; i++)
                {
                    var column = columns[i];
                    column.DisplayIndex = i;
                    column.DefaultCellStyle.BackColor = SystemColors.Window;
                }
            }            
            _arrangeByGroupsOrIndicatorPositionItem.Text = _bygroups ?
                Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_ATTRIBUTESBYINDICATORPOSITION :
                Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_ATTRIBUTESBYGROUPS;
        }

        /// <summary>
        /// Обрабатывает изменения в гриде обучающих записей
        /// </summary>
        /// <returns></returns>
        private List<ClassificationObjectInfo> ProcessChanges()
        {
            var rows = TrainingGrid.Rows;
            int attributesCount = TrainingGrid.Columns.Count - 1;
            List<ClassificationObjectInfo> infos = new List<ClassificationObjectInfo>();
            for (int i = 0; i < rows.Count; i++)
            {
                if (i == TrainingGrid.NewRowIndex)
                    continue;
                var row = rows[i];
                double[] attributes = new double[attributesCount];
                for (int j = 0; j < attributesCount; j++)
                {
                    int colIndex = j + 1;
                    var cell = row.Cells[colIndex];
                    double val = Constants.NullDouble;
                    object haveVal = cell.Value;
                    if (haveVal is double) //такое бывает если на момент загрузки диалога данные уже были, и их не изменили
                        val = (double)haveVal;
                    else
                    {
                        string valStr = cell.Value as string;
                        bool parsed = string.IsNullOrEmpty(valStr) || string.IsNullOrEmpty(valStr.Trim())
                            || double.TryParse(valStr, NumberStyles.Float, CultureInfo.CurrentUICulture, out val);
                        if (!parsed)
                        {
                            GeneralMethods.ShowUserError(Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_ATTRIBUTE_INCORRECTVALUE);
                            cell.Selected = true;
                            TrainingGrid.CurrentCell = cell;
                            TrainingGrid.BeginEdit(true);                            
                            return null;
                        }
                    }
                    attributes[j] = val;
                }
                var classCell = row.Cells[0] as DataGridViewComboBoxCell;
                int classIndex = classCell.Items.IndexOf(classCell.Value);
                infos.Add(new ClassificationObjectInfo(string.Empty, attributes, classIndex));
            }
            return infos;
        }

        /// <summary>
        /// Применяет изменения и закрывает диалог
        /// </summary>
        private void ApplyAndClose()
        {
            var infos = ProcessChanges();
            if (infos == null)
            {
                DialogResult = DialogResult.None;
                return;
            }
            //нельзя обучать сразу, иначе предыдущие правильно введенные строки потом трудно откатить                        
            try
            {
                _problem.Train(infos, true);
            }
            catch (BIGeometryImpossibleCalculateDistanceException ex)
            {
                GeneralMethods.ShowUserError(ex.Message);
                DialogResult = DialogResult.None;
                return;
            }
            DialogResult = DialogResult.OK;
            Close();
        }

        /// <summary>
        /// Отменяет изменения и закрывает диалог
        /// </summary>
        private void CancelAndClose()
        {            
            DialogResult = DialogResult.Cancel;
            Close();
        }

        /// <summary>
        /// Сбрасывает обучение задачи классификации
        /// </summary>
        private void ResetTrain()
        {            
            //if (GeneralMethods.ShowWarning(string.Format(Resources.BI_PROJECTEXPLORER_CLASSIFICATIONPROBLEM_WITHTRAINING_TRAIN_RESET, _problem.TrainingCacheCount), MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                TrainingGrid.Rows.Clear();                
                RefreshTrainingCount();                
            }
        }

        /// <summary>
        /// Обрабатывает добавление новой строки
        /// </summary>
        /// <param name="rowIndex"></param>
        private void ProcessAddRow(int rowIndex)
        {
            if (!_gridInitialized)
                return;
            int addedRowIndex = rowIndex - 1; //минус 1, т.к. добавленная строка - это NewRow, а интересующая нас редактируемая это предыдущая
            if (addedRowIndex > 0)
            {                
                var columns = TrainingGrid.Columns;
                for (int i = 0; i < columns.Count; i++)
                    TrainingGrid.Rows[addedRowIndex].Cells[i].Value = TrainingGrid.Rows[addedRowIndex - 1].Cells[i].Value;                
            }
            else
            {
                var classBox = TrainingGrid.Rows[rowIndex].Cells[0] as DataGridViewComboBoxCell;
                classBox.Value = classBox.Items[0] as string;
            }
            RefreshTrainingCount();
        }

        /// <summary>
        /// Обрабатывает удаление строки
        /// </summary>
        /// <param name="rowIndex"></param>
        private void ProcessDeleteRow(int rowIndex)
        {
            RefreshTrainingCount();            
        }        

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="problem"></param>
        public ClassificationProblemWithTrainingEditTrainingDialog(IClassificationProblemWithTraining problem, ProjectExplorerForm projectExplorer)
        {
            _problem = problem;
            _projectExplorer = projectExplorer;
            InitializeComponent();
        }

        private void OKButton_Click(object sender, EventArgs e)
        {
            ApplyAndClose();
        }

        private void CancelEditTrainingButton_Click(object sender, EventArgs e)
        {
            CancelAndClose();
        }

        private void ClassificationProblemWithTrainingEditTrainingDialog_Load(object sender, EventArgs e)
        {
            Initialize();
        }

        private void ResetTrainButton_Click(object sender, EventArgs e)
        {
            ResetTrain();
        }

        private void TrainingGrid_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            ProcessAddRow(e.RowIndex);
        }

        private void TrainingGrid_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            ProcessDeleteRow(e.RowIndex);
        }

        private void TrainingGrid_UserAddedRow(object sender, DataGridViewRowEventArgs e)
        {
            ProcessAddRow(e.Row.Index);
        }          
    }
}
